﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IdentityModel.Selectors;
using System.IdentityModel.Policy;
using System.Security.Principal;
using System.IdentityModel.Tokens;
using System.IdentityModel.Claims;

namespace VoluntariadoWindows8Backend.Security
{
    public abstract class CustomAuthorizationTemplate : UserNamePasswordValidator, IAuthorizationPolicy
    {
        #region IAuthorizationPolicy Members

        bool IAuthorizationPolicy.Evaluate(EvaluationContext evaluationContext, ref object state)
        {
            // will hold the combined roles
            List<string> roles = new List<string>();

            // get the authenticated client identity
            IIdentity client = GetClientIdentity(evaluationContext);

            // this policy is intended for custom accounts only
            GenericIdentity gid = client as GenericIdentity;
            if (gid == null)
            {
                throw new SecurityTokenValidationException("Only custom accounts supported");
            }

            // add application defined roles
            roles.AddRange(GetUserRoles(gid));

            // set a new principal holding the combined roles
            // this could be your own IPrincipal implementation
            evaluationContext.Properties["Principal"] = new CustomPrincipal(gid, roles.ToArray());

            return true;
        }

        ClaimSet IAuthorizationPolicy.Issuer
        {
            get { return ClaimSet.System; }
        }

        #endregion

        #region IAuthorizationComponent Members
        private Guid _id = Guid.NewGuid();

        string IAuthorizationComponent.Id
        {
            get { return _id.ToString(); }
        }

        #endregion

        #region UserNamePasswordValidator Overrides
        /// <summary>
        /// Sobrecarga del método <c>Validate</c> de la clase <c>UserNamePasswordValidator</c>.
        /// Simplemente invoca el método abstracto <c>ValidateUser</c> de esta misma clase que
        /// deberá ser definido en la implementación concreta que se haga de esta clase en el
        /// proyecto en que estemos usando la librería
        /// </summary>
        /// <param name="name">Nombre o login del usuario</param>
        /// <param name="password">Contraseña del usuario</param>
        public override void Validate(string name, string password)
        {
            if (!ValidateUser(name, password))
            {
                throw new SecurityTokenException();
            }
        }
        #endregion

        #region Abstract Methods
        /// <summary>
        /// Método abstracto a sobrecargar en la que devolveremos la lista de roles del usuario
        /// pasado como parametro cuyo nombre podemos conocer con la propiedad gid.Name
        /// </summary>
        /// <param name="gid">Objeto con la identidad ya autorizada</param>
        /// <returns>Lista de roles del usuario indicado en el parámetro</returns>
        protected abstract IEnumerable<string> GetUserRoles(GenericIdentity gid);
        /// <summary>
        /// Método abstracto a sobrecargar en la que devolveremos si el usuario supera el
        /// proceso de autorización o no
        /// </summary>
        /// <param name="name">Nombre o login del usuario</param>
        /// <param name="password">Contraseña del usuario</param>
        /// <returns><c>true</c> si el usuario se autoriza, <c>false</c> en caso contrario</returns>
        protected abstract bool ValidateUser(string name, string password);
        #endregion

        #region Private Methods
        private IIdentity GetClientIdentity(EvaluationContext evaluationContext)
        {
            IIdentity identity = null;
            object obj;

            if (evaluationContext.Properties.TryGetValue("Identities", out obj))
            {
                IList<IIdentity> identities = obj as IList<IIdentity>;
                if (identities != null && identities.Count > 0)
                    identity = identities[0];
            }

            if (identity != null)
            {
                return identity;
            }
            else
            {
                throw new Exception("No Identity found");
            }

        }
        #endregion
    }


}